#pragma once
#include "Pascal.h"
#include "wc3main.h"
#include "wc3memory.h"
#include "wc3string.h"
#include "wc3collections.h"
#include "wc3props.h"

procedure wc3jassvm_OnInit();
void wc3jassvm_main();

typedef Dword TScriptExitCode;

enum TScriptExitCode_Enum {
		scExecutionCompleted = 1,
		scOpLimitReached = 2,
		scSleeping = 3,
		scSyncing = 4,
		scFunctionNotFound = 5,
		scOverflowOrUndefined = 6,
		scZeroDivision = 7
};

static const char SCRIPT_TYPE_LETTER_VOID = 'V';
static const char SCRIPT_TYPE_LETTER_CODE = 'C';
static const char SCRIPT_TYPE_LETTER_INTEGER = 'I';
static const char SCRIPT_TYPE_LETTER_REAL = 'R';
static const char SCRIPT_TYPE_LETTER_STRING = 'S';
static const char SCRIPT_TYPE_LETTER_HANDLE = 'H';
static const char SCRIPT_TYPE_LETTER_BOOLEAN = 'B';

typedef Dword TScriptDataType;

enum TScriptDataType_Enum {
		stVoid = 0x00,
		stRetAddr = 0x01,
		stNull = 0x02,
		stCode = 0x03,

		stInteger = 0x04,
		stReal = 0x05,
		stString = 0x06,
		stHandle = 0x07,
		stBoolean = 0x08,

		stArrayOfInteger = 0x09,
		stArrayOfReal = 0x0A,
		stArrayOfString = 0x0B,
		stArrayOfHandle = 0x0C,
		stArrayOfBoolean = 0x0D
};


typedef Dword TOperationCode;

enum TOperationCode_Enum
{
		opEndProgram   = 0x01,
		opDebug        = 0x02,
		opFunction     = 0x03,
		opEndFunction  = 0x04,
		opLocal        = 0x05,
		opGlobal       = 0x06,
		opConstant     = 0x07,
		opArgument     = 0x08,
		opParentType   = 0x09,
		opChildType    = 0x0A,
		opCleanStack   = 0x0B,
		opLiteral      = 0x0C,
		opMove         = 0x0D,
		opGetVar       = 0x0E,
		opGetCode      = 0x0F,
		opGetArray     = 0x10,
		opSetVar       = 0x11,
		opSetArray     = 0x12,
		opPush         = 0x13,
		opPop          = 0x14,
		opNativeCall   = 0x15,
		opCall         = 0x16,
		opI2R          = 0x17,
		opAnd          = 0x18,
		opOr           = 0x19,
		opEqual        = 0x1A,
		opNotEqual     = 0x1B,
		opLesserEqual  = 0x1C,
		opGreaterEqual = 0x1D,
		opLesser       = 0x1E,
		opGreater      = 0x1F,
		opAdd          = 0x20,
		opSub          = 0x21,
		opMul          = 0x22,
		opDiv          = 0x23,
		opModulo       = 0x24,
		opNegate       = 0x25,
		opNot          = 0x26,
		opReturn       = 0x27,
		opLabel        = 0x28,
		opJumpIfTrue   = 0x29,
		opJumpIfFalse  = 0x2A,
		opJump         = 0x2B
};

	// functions

	class TNativeFunc
	{
	public:
		Pointer vtable;
		THashKeyData<PChar> HashKeyData;
		// field04: Dword;
		// field08: Dword;
		// field0C: Dword;
		// field10: Dword;
		// field14: Dword;
		// Name: PChar;
		Pointer Address;
		Dword ArgsCount;
		PChar Signature;
		Dword field28;
		Dword field2C;
		Dword field30;
		Dword field34;
		TScriptDataType rettype;
	};

	typedef TNativeFunc* PNativeFunc;

	class TFuncAddr : public TSHashObject<PChar>
	{
	public:
		// Hash: Dword;
		// field04: Dword;
		// field08: Dword;
		// field0C: Dword;
		// field10: Dword;
		// Name: PChar;
		Pointer Address;
		Dword RetType;
	};

	typedef TFuncAddr* PFuncAddr;
	
	struct TInitFuncAddr
	{
		typedef TSLinkedNode<TInitFuncAddr*> Node_Type2;
		Node_Type2 Node;
		Pointer Address;
	};

	typedef TInitFuncAddr* PInitFuncAddr;

	// script

	struct TInstruction
	{
		Byte r3, r2, r1, id;
		Dword Parameter;
	};

	typedef TInstruction* PInstruction;

	class THashedString : public TSHashObject<PChar>
	{
	public:
		Dword Index;
	};

	typedef THashedString* PHashedString;
	
	typedef TSGrowableArray<PHashedString> THashedStrings;
	typedef THashedStrings* PHashedStrings;

	//template <class THashedString, class THashedStrings, class TStringHashKeyManager>
	class THashedStringTable
	{
	public:
		typedef TSHashTable<PChar, THashedString, TStringHashKeyManager> FStrToItem_Type;
	public:
		THashedStrings FItems;
		FStrToItem_Type FStrToItem;
	public:
		PChar GetItem(Dword AIndex);
		Dword GetItemHash(Dword AIndex);
		Dword GetLength();
	public:
		Boolean GetItemIndex(PChar AItem, Dword &AIndex);
	public:
		//__declspec (property (get = GetItem)) PChar Items[];
		//__declspec (property (get = GetItemHash)) Dword ItemHashes[];
		//__declspec (property (get = GetLength)) Dword Length;
	};

	//typedef THashedStringTable<THashedString,THashedStrings,TStringHashKeyManager>* PHashedStringTable;
	typedef THashedStringTable* PHashedStringTable;

	//template <class PInstruction, class PHashedStringTable>
	struct TJassScript
	{
		PInstruction Code;
		SizeUInt CodeSize;
		PHashedStringTable Strings;
		Dword RiskLevel;
	};

	//typedef TJassScript<PInstruction,PHashedStringTable>* PJassScript;
	typedef TJassScript* PJassScript;

	// data

	//template <class TDefaultArrayItemManager>
	class TJassArray
	{
	public:
		typedef TSGrowableArray<Dword> List_Type;
	public:
		Pointer vtable;
		List_Type List;
	public:
		TJassArray();
		~TJassArray();
	};

	//typedef TJassArray<TDefaultArrayItemManager>* PJassArray;
	typedef TJassArray* PJassArray;

	//template <class PStringRep>
	class TJassString : public TRCString//<PStringRep>
	{
	public:
		//TRCString<PStringRep> FValue;
		TRCString FValue;
		Dword FReferences;
	public:
		procedure SetContent(PChar AContent);
		PChar GetContent();
	public:
		//__declspec (property (get = GetContent, put = SetContent)) PChar Content;
	public:
		TJassString();
		~TJassString();
	};

	//typedef TJassString<PStringRep>* PJassString;
	typedef TJassString* PJassString;
	
	typedef Dword TJassObjectHandle;
	typedef Dword TJassStringHandle;

	// hz
	typedef TSHashObject<PChar> TJassHandle;

	#ifndef TScriptData_H
	#define TScriptData_H
	//template <class THashKeyData>
	class TScriptData : public TSHashObject<PChar>
	{
	public:
		//typedef TSHashObject<PChar> TSHashObject_Char_Type;
	public:
		//TSHashObject_Char_Type vTSHashObject_Char;
	public:
		TScriptDataType InitializedType;
		TScriptDataType _Type;
		Dword Value;
		LongBool IsArgument;
	public:
		TScriptData();
		~TScriptData();
	};
	#endif

	//typedef TScriptData<THashKeyData<PChar>::PHashKeyData>* PScriptData;
	typedef TScriptData* PScriptData;

	#ifndef TFrameStackEnumerator_H
	#define TFrameStackEnumerator_H
	//template <class TScriptData>
	class TFrameStack;
	//typedef TFrameStack<TScriptData>* PFrameStack;
	typedef TFrameStack* PFrameStack;
	//template <class PFrameStack, class PScriptData>
	class TFrameStackEnumerator
	{
	public:
		PFrameStack FOwner;
		Dword FPosition;
	public:
		Boolean MoveNext();
		PScriptData GetCurrent();
		procedure Reset();
		//__declspec (property (get = GetCurrent)) PScriptData Content;
	public:
		TFrameStackEnumerator(PFrameStack AOwner);
	};
	#endif 

	#ifndef TFrameStack_H
	#define TFrameStack_H
	//template <class TScriptData>
	class TFrameStack
	{
	public:
		Dword Capacity;
		PScriptData Items[32];
		Dword Length;
	public:
		TFrameStack();
		~TFrameStack();
		TFrameStackEnumerator GetEnumerator();
	};
	#endif 
	
	//typedef TFrameStack<TScriptData>* PFrameStack;
	typedef TFrameStack* PFrameStack;

	//template <class THashKeyData, class TScriptData, class TConstantStringHashKeyManager>
	class TFrame
	{
	public:
		typedef TSLinkedNode<TFrame*> Node_Type;
		typedef TSHashTable<PChar, TScriptData, TConstantStringHashKeyManager> Locals_Type;
	public:
		Node_Type Node;
		TFrameStack Stack;
		Locals_Type Locals;
		LongBool IsFirstFrame;
	public:
		TFrame();
		~TFrame();
	};

	//typedef TFrame<THashKeyData<PChar>,TScriptData<THashKeyData<PChar>::PHashKeyData>,TConstantStringHashKeyManager>* PFrame;
	typedef TFrame* PFrame;

	// instance

	typedef Dword TJassFunctionHandle;
	typedef TSHashNode<PInstruction, TRCString> TRCStringNode;

	//TInstanceHandleTableItemManager = object
	//	class procedure InitilizeItem(var AItem: TItem; const AKey: TKey); static;
	//end;

	template <typename T>
	class TDefaultInstanceHandleTableItemManager
	{
	public:
		static procedure SetupItem(T AItem, const T AKey);
		static T ExtractItemKey(T AItem);
		static procedure ClearItem(T AItem);
	};

	//template <class PStringRep>
	class TJassStringInstanceHandleTableItemManager
	{
	public:
		static procedure SetupItem(TJassString AItem, const PChar AKey);
		static PChar ExtractItemKey(TJassString AItem);
		static procedure ClearItem(TJassString AItem);
	};

	template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
	class TInstanceHandleTable
	{
	public:
		typedef TItem* PItem;
		typedef TSGrowableArrayM<TItem, TItemArrayManager> TItems;
		typedef TItems* PItems;
		typedef Dword TItemHandle;
		typedef TSGrowableArray<TItemHandle> TItemHandles;
		typedef TItemHandles* PItemHandles;
		typedef TSHashNode<TKey, TItemHandle> TItemHandleNode;
		typedef TItemHandleNode* PItemHandleNode;
		typedef TSHashTable<TKey, TItemHandleNode, THashKeyManager> THandleByKeyTable;
		typedef THandleByKeyTable* PHandleByKeyTable;
	public: // Temp for debug
		PItems FItems;
		PItemHandles FFreeHandles;
		PHandleByKeyTable FHandleByKeyTable;
	public:
		TItem GetItem(TItemHandle AIndex);
		PItem GetItemPointer(TItemHandle AIndex);
		Dword GetItemsCount();
	public:
		//__declspec (property (get = GetItem)) TItem Items[];
		//__declspec (property (get = GetItemPointer)) PItem ItemPointers[];
		//__declspec (property (get = GetItemsCount)) Dword ItemsCount;
	public:
		TItemHandle FindHandle(const TKey AKey);
		TItemHandle GetHandle(const TKey AKey, Boolean &Created);
		TItemHandle GetHandle(const TKey AKey);
		procedure FreeHandle(TItemHandle AHandle);
	};

	//TVariableChangedCallback = procedure ({Name: PChar; NewValue: Dword;} Parameter: Pointer); stdcall; // fastcall
	typedef procedure (*TVariableChangedCallback)  (
		Pointer Dummy,
		Dword NewValue,
		PChar Name,
		Pointer Parameter
	); // fastcall

	enum TManageHandleAction {mhaReference, mhaUnreference};
	typedef procedure (__stdcall *TManageHandleFunction) (Pointer Parameter); // fastcall
	//TManageHandleFunction = procedure (Dummy: Pointer; Action: TManageHandleAction; hAgent: TJassObjectHandle; Parameter: Pointer); register; // fastcall
	
	#ifndef TInstance_H
	#define TInstance_H
	/*template
		<class THashKeyData,
		 class TScriptData,
		 class TConstantStringHashKeyManager,
		 class TSHashObject,
		 class TInstruction,
		 class TFuncAddr,
		 class THashlessKeyManager,
		 class TDefaultArrayItemManager,
		 class TJassStringInstanceHandleTableItemManager,
		 class TFrame>*/
	class TInstance
	{
	public:
		typedef TInstance* PInstance;
		typedef TScriptData* TRegisters[256];
		typedef TRegisters* PRegisters;
		typedef TSHashTable<PChar, TScriptData, TConstantStringHashKeyManager> TVariables;
		typedef TVariables* PVariables;
		typedef TSHashTable<PChar, TJassHandle, TConstantStringHashKeyManager> TStrToJassHandle;
		typedef TStrToJassHandle* PStrToJassHandle;
		typedef TSGrowableArray<PInstruction> TCodePtrs;
		typedef TCodePtrs* PCodePtrs;
		typedef TArrayOfObjectsManager<TJassString> TJassStringArrayItemManager;
		typedef TSLinkedList<TInitFuncAddr, TMissingInitialOffset> TInitFuncs;
		typedef TInitFuncs* PInitFuncs;
		typedef TSHashTable<PChar, TFuncAddr, TConstantStringHashKeyManager> TNameToFunc;
		typedef TNameToFunc* PNameToFunc;
		typedef TSHashTable<PInstruction, TRCStringNode, THashlessKeyManager> TCodePtrToName;
		typedef TCodePtrToName* PCodePtrToName;
		typedef TInstanceHandleTable<TJassString, TJassStringInstanceHandleTableItemManager, TJassStringArrayItemManager, PChar, TConstantStringHashKeyManager> TStringTable;
		typedef TInstanceHandleTable<PInstruction, TDefaultInstanceHandleTableItemManager<PInstruction>, TDefaultArrayItemManager, PInstruction, THashlessKeyManager> TCodeTable;

		typedef TSGrowableArray<PInstruction> CodeStack_Type;
		typedef TSLinkedList<TFrame, TMissingInitialOffset> Frames_Type;
	public:
		Dword field00;
		Dword field04;
		Dword field08;
		Dword field0C;
		Dword field10;
		Dword field14;
		LongBool IsChild;
		Dword ChildsCount;
		PInstruction CurrentOpcode;
		CodeStack_Type CodeStack;
		LongBool Sleeping;
		Pointer SleepParam;
		LongBool Syncing;
		Single SleepTimeout;
		Dword OperationsLimit;
		LongBool UntrustedCode;
		Dword RegistersCount;
		TScriptData Registers[256];
		Dword Handle;
		Dword ParentHandle;
		PJassScript Script;
		PVariables Globals;
		Frames_Type Frames;
		PStrToJassHandle unk_StrToJassHandleMap;
		PCodePtrs Labels;
		TStringTable StringTable;
		PInitFuncs InitFuncs;
		PNameToFunc NameToFuncAddr;
		TCodeTable CodeTable;
		PCodePtrToName CodePtrToName;
		TVariableChangedCallback VarChangedCallback;
		Pointer VarChangedCallbackParam;
		TManageHandleFunction RefHandleCallback;
		Pointer RefHandleCallbackParam;
	public:
		procedure SetScriptDataValue(TScriptData AData, Dword ANewValue, TScriptDataType ANewType);
		procedure ChangeValueOfType(Dword ADestination, TScriptDataType AType, Dword AValue);
		procedure ClearJassArrayItems(PJassArray JassArray, TScriptDataType ItemType);
		procedure ClearScriptData(PScriptData Data);
		procedure IncrementHandleReferences(TJassObjectHandle hAgent);
		procedure DecrementHandleReferences(TJassObjectHandle hAgent);
		procedure IncrementStringReferences(TJassStringHandle hString);
		procedure DecrementStringReferences(TJassStringHandle hString);
		TInstance* GetTopmostParent();
	};
	#endif

	/*typedef TInstance
		<THashKeyData<PChar>,
		 TScriptData,
		 TConstantStringHashKeyManager,
		 TSHashObject<PChar>,
		 TInstruction,
		 TFuncAddr,
		 THashlessKeyManager,
		 TDefaultArrayItemManager,
		 TJassStringInstanceHandleTableItemManager<PStringRep>,
		 TFrame>* PInstance;*/
	typedef TInstance* PInstance;
	// environment

	typedef Dword TInstanceHandle;
	typedef Dword TScriptHandle;

	typedef TSHashNode<PJassScript, TScriptHandle> TScriptHandleNode;

	class TJassThreadLocal
	{
	public:
		typedef TSHashTable<PChar, TNativeFunc, TStringHashKeyManager> Natives_Type;
		typedef TSHashTable<PJassScript, TScriptHandleNode, THashlessKeyManager> ScriptHandles_Type;
	public:
		Pointer vtable;
		TSGrowableArray<PInstance> RunningInstances;
		Int32 RunningInstancesCount;
		Natives_Type Natives;
		TSGrowableArray<PJassScript> Scripts;
		TSGrowableArray<TScriptHandle> FreeScripts;
		ScriptHandles_Type ScriptHandles;
	  	TSGrowableArray<PInstance> Instances;
		TSGrowableArray<TInstanceHandle> FreeInstances;
		Dword fieldA8, fieldAC;
	};

	typedef TJassThreadLocal* PJassThreadLocal;

static const Dword JASS_MAX_ARRAY_SIZE = 8192;
static const Dword DEFAULT_JASS_OPERATION_LIMIT = 300000;

	typedef procedure (__stdcall *_RegisterNative_1) (PChar Signature);
	typedef TScriptHandle (__stdcall *_Compile_1) (Dword ErrorCallbackParam, Dword RiskLevel);
	typedef procedure (__stdcall *_FreeScript_1) ();
	typedef TInstanceHandle (__stdcall *_CreateInstance_1) ();
	typedef TInstanceHandle (__stdcall *_RegisterInstance_1) ();
	typedef procedure (__stdcall *_FreeInstance_1) (Integer Index, Pointer Container);
	typedef TScriptExitCode (__stdcall *_RunInstance_1) (Dword &Result, LongBool CallInit, LongBool Resume, Dword OpLimit);
	typedef TInstanceHandle (__stdcall *_CloneInstance_1) ();
	typedef TScriptExitCode (__stdcall *_Instance_Run_1) (PInstruction Code, PDword Result, Dword OpLimit, Dword Unused);
	typedef TScriptExitCode (__stdcall *_Instance_CallCode_1) (PInstruction pCode, Pointer Parameters, Dword &Result, Dword OpLimit, Dword Unused, LongBool DontResume);
	typedef TScriptExitCode (__stdcall *_Instance_ExecuteFunctionWithOptionalInit_1) (PChar FunctionName, PDword Result, LongBool InitGlobals, Dword OpLimit, Dword Unused);
	typedef TScriptExitCode (__stdcall *_ExecuteFunctionByHandle_1) (Pointer Parameters, PDword ScriptResult, Dword OpLimit, Dword Unused, LongBool ForceNewRun);
	typedef TJassStringHandle (__stdcall *_Instance_GetStringHandle_1) (PChar Value);

	//_RegisterNative_1 _RegisterNative;
	//_Compile_1 _Compile;
	//_FreeScript_1 _FreeScript;
	//_CreateInstance_1 _CreateInstance;
	//_RegisterInstance_1 _RegisterInstance;
	//_FreeInstance_1 _FreeInstance;
	//_RunInstance_1 _RunInstance;
	//_CloneInstance_1 _CloneInstance;
	//_Instance_Run_1 _Instance_Run;
	//_Instance_CallCode_1 _Instance_CallCode;
	//_Instance_ExecuteFunctionWithOptionalInit_1 _Instance_ExecuteFunctionWithOptionalInit;
	//_ExecuteFunctionByHandle_1 _ExecuteFunctionByHandle;
	//_Instance_GetStringHandle_1 _Instance_GetStringHandle;

	//Pointer JassArray_VMT;

PJassThreadLocal GetJassThreadLocal();
Boolean GetScriptDataTypeByLetter(Char &Letter, TScriptDataType &DataType);

